﻿//========================================================= 
/**@file CommandInstaller.h
 * @brief 
 * 
 * @date 2016-10-19   09:39:51
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_COMMANDINSTALLER_H_
#define _LIBZHOUYB_COMMANDINSTALLER_H_
//--------------------------------------------------------- 
#include "CommandRunner.h"

#include "device/LoggerCmdDriver.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace driver {
//--------------------------------------------------------- 
/// 命令安装工具
class CommandInstaller
{
protected:
    CommandInstaller() {}
public:
    /// 剔除由 LC_CMD_METHOD 之外的宏定义的系统宏
    static CommandSetter& ExceptSysCommand(CommandSetter& setter, CommandSetter* pSysSetter = NULL)
    {
        // 删除通信层接口命令
        setter.Except("IInterface;IFactory;IInterrupter;IBaseDevice;IInteractiveTrans;ITransceiveTrans;IICCardDevice;ILogger", pSysSetter)
            .Except("IBehavior;ITimeoutBehavior;InterruptBehavior;ILastErrBehavior;ILoggerBehavior;IBaseDevAdapterBehavior", pSysSetter)
            .Except("SelectDevice;ReleaseDevice;ActiveDevice", pSysSetter)
            .Except("LastError;ResetError;SetLastError", pSysSetter)
            .Except("SetInterrupter", pSysSetter)
            .Except("Interrupt;Implement;Cancel;InterruptReset", pSysSetter)
            .Except("SetTimeoutMS", pSysSetter)
            .Except("SelectLogger;ReleaseLogger", pSysSetter)
            .Except("EnumCommand;OnCommand;ExceptCommand;PrintCommand", pSysSetter)
            .Except("CompileVersion", pSysSetter);
        return setter;
    }
    /// 装载日志
    static bool InstallLOG(CommandRunner& runner, const char* dir = NULL, 
        const char* name = NULL, size_t fileCount = LOG_FILE_COUNT, size_t fileSize = LOG_FILE_SIZE)
    {
        string sDir = _is_empty_or_null(dir) ? "." : dir;
        if(sDir[sDir.length() - 1] != PATH_SEPARATOR)
            sDir += PATH_SEPARATOR;

        Ref<ComplexCommand> complexCmd;
        StringArguments arg;
        StringArguments rlt;
        arg.Put("Folder", sDir);
        arg.Put("Count", ArgConvert::ToString<size_t>(fileCount));
        arg.Put("Size", ArgConvert::ToString<size_t>(fileSize));

        string sName = "";
        if(!_is_empty_or_null(name))
        {
            sName += name;
            sName += "_";
        }
        string sTitle = sName;
        sTitle += "Command";
        string& argTitle = arg.Put("Title", sTitle);
        Ref<LoggerCmdDriver> logDrv;

        const char LoggerKey[] = "SelectLogger";
        const char LogGrpKey[] = "LOG";
        const char CompileVersionKey[] = "CompileVersion";

        string logName = LogGrpKey;
        logName += ".Runner";
        // 输出当前注册的全部命令
        LOGGER(runner.New(logName, &logDrv);
        if(!logDrv->Open(arg, rlt))
        {
            runner.Remove(logName);
            return false;
        }
        rlt.Clear();
        logDrv->Ref_LoggerAdapter(arg, rlt);
        Ref<LoggerAdapter> refLog = CommandReflectArg(LoggerAdapter, rlt);

        FileLogger filelog;
        LoggerAdapter _log;
        string cmdTree = sDir;
        cmdTree += "CommandTree.log";
        filelog.Open(cmdTree.c_str());
        filelog.Clear();
        _log.Select(filelog);
        _log.WriteLine(LOGGER_SPLIT_LINE);
        _log.WriteTime().WriteLine("Command Tree:");
        runner.Print(_log);
        _log.WriteLine(LOGGER_SPLIT_LINE));
        
        list_t<ArgValue<string, Ref<CommandCollection> > > listCollection;
        Ref<CommandCollection> pCollection = runner;
        list_t<ArgValue<string, Ref<CommandCollection> > >::iterator itr;
        itr = listCollection.push_back();
        itr->Key = sName;
        itr->Key += "Runner";
        itr->Value = pCollection;

        LOGGER(_log.WriteLine(LOGGER_SPLIT_LINE);
        _log.WriteLine("Module:"));
        // 查找是否有 SelectLogger 接口
        CommandRunner::EnumeratorType enumer = runner.GetEnumerator();
        while(enumer.MoveNext())
        {
            pCollection = runner.Item(enumer.Current());
            if(!pCollection.IsNull())
            {
                itr = listCollection.push_back();
                itr->Key = sName;
                itr->Key += enumer.Current();
                itr->Value = pCollection;
            }
            LOGGER(_log << enumer.Current() << ' ');
        }
        LOGGER(_log.WriteLine().WriteLine(LOGGER_SPLIT_LINE));

        LOGGER(
        list_t<Ref<ComplexCommand> > cmdlist;
        list_t<Ref<ComplexCommand> >::iterator cmdItr);

        Ref<LoggerAdapter> refRunnerLog;
        for(itr = listCollection.begin();itr != listCollection.end(); ++itr)
        {
            pCollection = itr->Value;
            argTitle = itr->Key;

            LOGGER(cmdlist.clear();
            pCollection->GetCommand("", cmdlist);
            for(cmdItr = cmdlist.begin();cmdItr != cmdlist.end(); ++cmdItr)
            {
                (*cmdItr)->SelectLogger(refLog);
            });

            complexCmd = pCollection->LookUp(LoggerKey);
            if(complexCmd.IsNull())
                continue;

            logName = LogGrpKey;
            logName += ".";
            logName += argTitle;

            runner.New(logName, &logDrv);
            if(!logDrv->Open(arg, rlt))
            {
                runner.Remove(logName);
                continue;
            }
            rlt.Clear();
            logDrv->Ref_LoggerAdapter(arg, rlt);
            complexCmd->OnCommand(rlt, arg);

            // 为每个模块的日志中写入编译版本标识
            rlt.Clear();
            complexCmd = pCollection->LookUp(CompileVersionKey);
            if(complexCmd.IsNull())
                continue;
            
            refRunnerLog = logDrv->GetLoggerAdapter();
            if(refRunnerLog.IsNull())
                continue;

            complexCmd->OnCommand(arg, rlt);
            (*refRunnerLog).WriteLine(LOGGER_SPLIT_LINE).WriteTime();
            (*refRunnerLog) << "Compile Version:<" << rlt["Version"].Value << ">\n";
            (*refRunnerLog).WriteLine(LOGGER_SPLIT_LINE);
            (*refRunnerLog).Flush();
        }
        return true;
    }
    /**
     * @brief 设置日志开启状态(反射LoggerCmdDriver::SetEnable进行开关设置)
     * @date 2017-03-22 11:27
     * 
     * @param [in] runner 需要设置的CommandRunner
     * @param [in] isEnable 是否需要开启日志
     * @param [in] module [default:NULL] 需要操作的模块(模块之间以;号分隔)
     * 
     * @return bool 返回runner是否支持日志功能
     */
    static bool EnableLOG(CommandRunner& runner, bool isEnable, const char* module = NULL)
    {
        const char LOG_INTERFACE_KEY[] = "LoggerAdapter";
        const char LOG_SET_ENABLE_KEY[] = "SetEnable";
        // 反射日志
        CommandRunner::EnumeratorType enumer = runner.GetEnumerator();
        Ref<CommandCollection> pCollection;
        Ref<ComplexCommand> complexCmd;
        bool isContainsLOG = false;
        list_t<string> moduleNames;
        list_t<string>::iterator itr;
        if(!_is_empty_or_null(module))
        {
            StringHelper::Split(module, moduleNames, ';');
        }

        StringArguments arg;
        StringArguments rlt;
        arg.Put("IsEnable", ArgConvert::ToString(isEnable));

        while(enumer.MoveNext())
        {
            // 过滤非日志模块
            if(!StringConvert::StartWith(enumer.Current().c_str(), "LOG.", true))
                continue;

            if(moduleNames.size() > 0)
            {
                ByteArray currentName(enumer.Current().c_str(), enumer.Current().length());
                bool isModule = false;
                for(itr = moduleNames.begin();itr != moduleNames.end(); ++itr)
                {
                    if(StringConvert::EndWith(currentName, itr->c_str(), true))
                    {
                        isModule = true;
                        break;
                    }
                }
                // 不需要操作该模块
                if(!isModule)
                    continue;
            }
            pCollection = runner.Item(enumer.Current());
            if(pCollection.IsNull())
                continue;
            complexCmd = pCollection->LookUp(LOG_INTERFACE_KEY);
            // 不是日志模块
            if(complexCmd.IsNull())
                continue;
            // SetEnable
            complexCmd = pCollection->LookUp(LOG_SET_ENABLE_KEY);
            if(complexCmd.IsNull())
                continue;
            isContainsLOG = true;
            complexCmd->OnCommand(arg, rlt);
        }
        return isContainsLOG;
    }
    /// 给指定的子模块注册日志
    static Ref<LoggerCmdDriver> RegisteLOG(CommandRunner& runner, const char* module)
    {
        const char LoggerKey[] = "SelectLogger";
        const char LogGrpKey[] = "LOG";

        Ref<CommandCollection> pCollection = runner.Item(_strput(module));
        if(pCollection.IsNull())
            return Ref<LoggerCmdDriver>();

        Ref<ComplexCommand> complexCmd = pCollection->LookUp(LoggerKey);
        if(complexCmd.IsNull())
            return Ref<LoggerCmdDriver>();

        StringArguments arg;
        StringArguments rlt;
        Ref<LoggerCmdDriver> logDrv;

        string logName = LogGrpKey;
        if(!_is_empty_or_null(module))
        {
            logName += ".";
            logName += module;
        }

        runner.New(logName, &logDrv);
        logDrv->Ref_LoggerAdapter(arg, rlt);
        LOGGER(Ref<LoggerAdapter> refLog = CommandReflectArg(LoggerAdapter, rlt));
        complexCmd->OnCommand(rlt, arg);

        LOGGER(list_t<Ref<ComplexCommand> > cmdlist;
        list_t<Ref<ComplexCommand> >::iterator cmdItr;
        cmdlist.clear();
        pCollection->GetCommand("", cmdlist);
        for(cmdItr = cmdlist.begin();cmdItr != cmdlist.end(); ++cmdItr)
        {
            (*cmdItr)->SelectLogger(refLog);
        });

        return logDrv;
    }
    /// 打开日志
    static bool OpenLOG(Ref<LoggerCmdDriver> logDrv, const char* name, 
        const char* dir = NULL, size_t fileCount = LOG_FILE_COUNT, size_t fileSize = LOG_FILE_SIZE)
    {
        if(logDrv.IsNull())
            return false;

        string sDir = _is_empty_or_null(dir) ? "." : dir;
        StringArguments arg;
        StringArguments rlt;
        arg.Put("Folder", sDir);
        arg.Put("Count", ArgConvert::ToString<size_t>(fileCount));
        arg.Put("Size", ArgConvert::ToString<size_t>(fileSize));
        arg.Put("Title", _strput(name));
        return logDrv->Open(arg, rlt);
    }
    /// 剔除不需要配置的私有命令
    static size_t ExceptCommand(CommandSetter& setter, CommandSetter* pExceptSetter = NULL)
    {
        StringArguments arg;
        StringArguments rlt;
        list_t<Ref<ComplexCommand> > cmds;
        setter.Cut("ExceptCommand").Export(cmds);

        list_t<Ref<ComplexCommand> >::iterator cmdItr;
        for(cmdItr = cmds.begin();cmdItr != cmds.end(); ++cmdItr)
        {
            (*cmdItr)->OnCommand(arg, rlt);
        }
        list_t<string> names;
        list_t<string>::iterator itr;
        rlt.Get("Cmd", names);

        size_t count = 0;
        for(itr = names.begin();itr != names.end(); ++itr)
        {
            setter.Except(itr->c_str(), pExceptSetter);
            ++count;
        }
        return count;
    }
    /// 过滤命令后注册新模块
    static CommandSetter Registe(CommandSetter& setter, CommandSetter& module, const char* moduleName, CommandSetter* pExceptSetter = NULL)
    {
        CommandInstaller::ExceptSysCommand(module);
        CommandSetter exceptSetter;
        CommandInstaller::ExceptCommand(module, &exceptSetter);
        CommandSetter setterNew = setter.Registe(moduleName, module);
        setter.Registe(moduleName, exceptSetter);
        if(pExceptSetter != NULL)
        {
            pExceptSetter->Get(exceptSetter);
        }
        return setterNew;
    }
    /**
     * @brief 注册新模块,并调用TModule::Except剔除不需要外部做额外设置的命令接口
     * @date 2016-11-02 00:12:47
     */
    template<class TModule>
    static CommandSetter RegisteNew(CommandRunner& runner, const char* moduleName, Ref<TModule>* pModule = NULL)
    {
        CommandSetter setter = runner;
        string name = _strput(moduleName);
        CommandSetter moduleSetter = runner.template New<TModule>(name, pModule).All();
        CommandSetter exceptSetter;
        CommandSetter setterNew = CommandInstaller::Registe(setter, moduleSetter, moduleName, &exceptSetter);
        if(exceptSetter.Count() > 0)
        {
            Ref<ComplexCommand> exceptCmd = runner.Registe("ExceptCommand");
            Ref<CommandScriptActuator> refExcept;

            string exceptName = "Script.";
            exceptName += name;
            exceptName += ".Except";
            runner.template New<CommandScriptActuator>(exceptName, &refExcept);
            StringArguments arg;
            CommandSetter::EnumeratorType enumer = exceptSetter.GetEnumerator();
            string cmdname = "";
            if(name.length() > 0)
                name += ".";
            while(enumer.MoveNext())
            {
                cmdname = name;
                cmdname += enumer.Current()->Name();
                arg.Put("CMD", cmdname);
            }
            refExcept->Run(_MakeCMDF(CommandScript, ARGtoRLT), arg);
            refExcept->Export(true);
            exceptCmd->Bind(refExcept->ScriptCmd("ExceptCommand"));
        }
        return setterNew;
    }
    /// 支持带命名空间的查找命令(命令空间以::分隔)
    static Ref<ComplexCommand> LookUp(CommandRunner& runner, const char* cmd, ByteBuilder* pSpaceName = NULL)
    {
        // 分隔 IC::GetCardNumber
        ByteBuilder spaceName;
        ByteArray cmdName(cmd);
        size_t index = StringConvert::IndexOf(cmdName, "::");
        if(index != SIZE_EOF)
        {
            spaceName = cmdName.SubArray(0, index);
            cmdName = cmdName.SubArray(index + 2);
        }

        string item;
        Ref<CommandCollection> pVal;
        Ref<ComplexCommand> complexCmd;

        if(!spaceName.IsEmpty())
        {
            item = spaceName.GetString();
            pVal = runner.Item(item);
            if(!pVal.IsNull())
            {
                complexCmd = pVal->LookUp(cmdName);
            }
        }
        else
        {
            // 在模块中部分查找
            complexCmd = runner.LookUp(cmdName);
            if(complexCmd.IsNull())
            {
                CommandRunner::EnumeratorType enumer = runner.GetEnumerator();
                while(enumer.MoveNext())
                {
                    pVal = runner.Item(enumer.Current());
                    if(pVal.IsNull())
                        continue;
                    complexCmd = pVal->LookUp(cmdName);
                    if(!complexCmd.IsNull())
                    {
                        spaceName = item.c_str();
                        break;
                    }
                }
            }
        }
        if(pSpaceName != NULL)
        {
            pSpaceName->Append(spaceName);
        }
        return complexCmd;
    }
    /// 获取错误码和错误信息
    static int LastError(CommandRunner& runner, ByteBuilder& err)
    {
        StringArguments rlt;
        CommandRunnerActuator actuator(runner);
        actuator.Run("LastError").Result(rlt).Reset();
        string msg = rlt["MSG"].To<string>();
        err += msg.c_str();

        int code = rlt["CODE"].To<int>();
        return code;
    }
    /**
     * @brief 返回命令是否为Except类型的命令
     * @date 2017-06-19 21:55
     * 
     * @param [in] setter 需要检测的命令集
     * @param [in] cmd 需要判断的命令名称
     * @param [in] isIgnoreCase 在比较命令名称时是否忽略大小写匹配
     */
    static bool IsExceptCommand(CommandSetter& setter, const char* cmd, bool isIgnoreCase = true)
    {
        Ref<ComplexCommand> complexCmd = setter.LookUp(cmd);
        // 没有查找到命令,如果存在多个,命令无法执行,不需要考虑命令名称冲突的情况
        if(complexCmd.IsNull())
            return true;
        CommandSetter exceptSetter;
        ExceptSysCommand(setter, &exceptSetter);
        ExceptCommand(setter, &exceptSetter);

        CommandSetter::EnumeratorType enumer = exceptSetter.GetEnumerator();
        ByteArray cmdName(cmd);
        while(enumer.MoveNext())
        {
            if(StringConvert::Compare(enumer.Current()->Name(), cmdName, isIgnoreCase))
                return true;
        }
        return false;
    }
    /// 获取指定模块的脚本编辑器
    static Ref<CommandScriptActuator> NewScriptCMD(CommandRunner& runner, const char* devId, string* pFullName = NULL)
    {
        string scriptGrpName = "Script";
        string scriptName;
        if(!_is_empty_or_null(devId))
        {
            scriptGrpName += ".";
            scriptGrpName += devId;
        }
        scriptGrpName += ".Setter";

        Ref<CommandCollection> devCollection;
        Ref<CommandScriptActuator> devScript;
        string setterName;
        for(size_t i = 0;;++i)
        {
            setterName = scriptGrpName;
            setterName += "[";
            setterName += ArgConvert::ToString(i);
            setterName += "]";

            devCollection = runner.Item(setterName);
            if(devCollection.IsNull())
            {
                if(pFullName != NULL)
                {
                    (*pFullName) = setterName;
                }
                runner.New<CommandScriptActuator>(setterName.c_str(), &devScript);
                break;
            }
        }
        return devScript;
    }
};
//--------------------------------------------------------- 
} // nemespace driver
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_COMMANDINSTALLER_H_
//========================================================= 